package cn.xydpeng.springframework.aop.framework;

import cn.xydpeng.springframework.aop.Advisor;
import cn.xydpeng.springframework.util.Assert;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Base class for AOP proxy configuration managers.
 * These are not themselves AOP proxies, but subclasses of this class are
 * normally factories from which AOP proxy instances are obtained directly.
 *
 * <p>This class frees subclasses of the housekeeping of Advices
 * and Advisors, but doesn't actually implement proxy creation
 * methods, which are provided by subclasses.
 *
 * <p>This class is serializable; subclasses need not be.
 * This class is used to hold snapshots of proxies.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see org.springframework.aop.framework.AopProxy
 */
public class AdvisedSupport extends ProxyConfig {

    private List<Class<?>> interfaces = new ArrayList<>();

    private List<Advisor> advisors = new ArrayList<>();

    /** Cache with Method as key and advisor chain List as value. */
    private transient Map<MethodCacheKey, List<Object>> methodCache;

    /**
     * Add a new proxied interface.
     * @param intf the additional interface to proxy
     */
    public void addInterface(Class<?> intf) {
        Assert.notNull(intf, "Interface must not be null");
        if (!intf.isInterface()) {
            throw new IllegalArgumentException("[" + intf.getName() + "] is not an interface");
        }
        if (!this.interfaces.contains(intf)) {
            this.interfaces.add(intf);
            adviceChanged();
        }
    }

    // @Override
    public Class<?>[] getProxiedInterfaces() {
        return this.interfaces != null ? interfaces.toArray(new Class<?>[0]): new Class<?>[0];
    }

    /**
     * Invoked when advice has changed.
     */
    protected void adviceChanged() {
        this.methodCache.clear();
    }















    /**
     * Simple wrapper class around a Method. Used as the key when
     * caching methods, for efficient equals and hashCode comparisons.
     */
    private static final class MethodCacheKey implements Comparable<MethodCacheKey> {
        private final Method method;
        private final int hashCode;

        public MethodCacheKey(Method method) {
            this.method = method;
            this.hashCode = method.hashCode();
        }
        @Override
        public boolean equals(Object other) {
            return (this == other || (other instanceof MethodCacheKey methodCacheKey &&
                    this.method == methodCacheKey.method));
        }
        @Override
        public int hashCode() {
            return this.hashCode;
        }
        @Override
        public String toString() {
            return this.method.toString();
        }
        @Override
        public int compareTo(MethodCacheKey other) {
            int result = this.method.getName().compareTo(other.method.getName());
            if (result == 0) {
                result = this.method.toString().compareTo(other.method.toString());
            }
            return result;
        }
    }

}
